#include "CamHandler.h"
#include <math.h>
#include <iostream>
#include "Trigon.h"
#include "CamHandlerThread.h"

using namespace osg;
using namespace std;
CamHandlerThread * threadsC[11];


/*
 * Konstruktor. Bekommt die Kamera, ein Clipping-Objekt, ein HUD-Objekt
 * und die Kamerakoordinten übergeben. Mappt die benutzen Knöpfe auf
 * Methoden und initialisiert ein Thread-Array für die Bewegungs- und
 * Blickthreads
 */

CamHandler::CamHandler(osg::Camera* cam, Clip* clip, HUD * hud, osg::Vec3d eye,
		osg::Vec3d center, osg::Vec3d up):
        _cam(cam), _clip(clip),_hud(hud), _eye(eye),_center(center), _up(up)  {
	azimut = 180;
	pol = 90;
	step = 1.0f;
	locked = false;

	updateCam();

	keyToHandle('w', STEPFORWARD);
	keyToHandle('s', STEPBACKWARD);
	keyToHandle('a', STEPLEFT);
	keyToHandle('d', STEPRIGHT);
	keyToHandle('i', LOOKUP);
	keyToHandle('k', LOOKDOWN);
	keyToHandle('j', LOOKLEFT);
	keyToHandle('l', LOOKRIGHT);
	keyToHandle('f', STEPUP);
	keyToHandle('v', STEPDOWN);
	keyToHandle('c', SWITCHCLIP);

	for (int i = 0; i < 11; i++){
		threadsC[i] = new CamHandlerThread(this, i, 10000);
		threadsC[i]->quit();
	}
}

/*
 * Wird aufgerufen, wenn eine Taste gedückt oder losgelassen wurde.
 * Wurde eine gemappte Taste gedrückt, wird der zugehörige
 * Bewegungs- oder Blickthread gestartet. Wird eine Taste losgelassen,
 * wird der zugehörige Thread gestoppt.
 */

bool CamHandler::handle(const osgGA::GUIEventAdapter &ea,
	osgGA::GUIActionAdapter &aa) {
if (!_cam) return false;

	newKeyDownEvent = false;
	newKeyUpEvent   = false;

	 switch(ea.getEventType()) {
	   case(osgGA::GUIEventAdapter::KEYDOWN):{

		 itr = keyFuncMap.find(ea.getKey());
		 if (itr != keyFuncMap.end()){

		    if ( (*itr).second.keyState == KEY_UP ){
				(*itr).second.keyState = KEY_DOWN;
				newKeyDownEvent = true;
		    }

		    if (newKeyDownEvent){
				m = (*itr).second.keyAction;
				threadsC[m]->reset();
				threadsC[m]->start();
				newKeyDownEvent = false;
		    }
		    return true;
		 }
		 return false;
	   }
	   case(osgGA::GUIEventAdapter::KEYUP):{

		 itr = keyFuncMap.find(ea.getKey());
		 if (itr != keyFuncMap.end() ){
			(*itr).second.keyState = KEY_UP;
		 }

		 itr = keyUPFuncMap.find(ea.getKey());

		 if (itr != keyUPFuncMap.end()){
			m = (*itr).second.keyAction;
			threadsC[m]->quit();
			return true;
		 }
		 return false;
	   }

	   default:
	      return false;
	   }
}

CamHandler::~CamHandler(){
#ifdef DEBUG
	cout << "----- Destruiert: CamHandler -----" << endl;
#endif
}

/*
 * Fügt Tasten in die keymaps ein.
 */

void CamHandler::keyToHandle(const int k, const MOVEACTION a){
	keyFuncMap[k].keyAction = a;
	keyUPFuncMap[k].keyAction = a;
}

/*
 * Zugriff auf den gemeinsamen Mutex für alle Threads
 */

OpenThreads::Mutex * CamHandler::getLockMutex(){
	return &lockmutexC;
}

/*
 * Clippingschalter
 */

void CamHandler::clipSwitch() {
	_clip->clipSwitch();
}

/*
 * Methode zum Ausgeben der Koordinaten auf der Konsole.
 * Hat nur bei gesetztem DEBUG Switch eine Funktion.
 */


void CamHandler::printCoords(const char key){
	#ifdef DEBUG
	cout << "----------------------------" <<
		"-----------------------------" << endl;
	cout << "azimut:" << azimut << " " << sinDeg(azimut) << endl;
	cout << "pol:" << pol << " " << sinDeg(pol) << endl;
	cout << "_eye:" << "[" << _eye.x() << ","
		<< _eye.y() << "," << _eye.z() << "]" << endl;
	cout << "_center:" << "[" << _center.x() << ","
		<< _center.y() << "," << _center.z() << "]" << endl;
	cout << "_up:" << "[" << _up.x() << ","
		<< _up.y() << "," << _up.z() << "]" << endl;
	cout << key << "-Taste gedrueckt" << "["
		<< _eye.x() << "," << _eye.y() << ","
			<< _eye.z() << "]" << endl;
	#endif
}

/*
 * Updatemethode für die Kamera. Wird von den Bewegungs- und
 * Blickmethoden nach setzen der neuen Koordinaten aufgerufen.
 */

void CamHandler::updateCam()
{
	_center = _eye + Vec3d(sinDeg(pol)*sinDeg(azimut),
		sinDeg(pol)*cosDeg(azimut), cosDeg(pol));
	_cam->setViewMatrixAsLookAt(_eye, _center, _up);
}

/*
 * Methoden zum Anpassen des Azimut- und Polarwinkels
 */

void CamHandler::incAz(){
	if (azimut == 359) azimut = 0;
	else azimut++;
}

void CamHandler::decAz(){
	if (azimut == 0) azimut = 359;
	else azimut--;
}

void CamHandler::incPo(){
	if (pol == 179) return;
	else pol++;
}

void CamHandler::decPo(){
	if (pol == 1) return;
	else pol--;	
}

/*
 * ------------------------------------------------------------------
 * 						BEWEGUNGS- UND BLICKMETHODEN
 * ------------------------------------------------------------------
 *
 */

void CamHandler::stepForward() {
	_deltaEye = Vec3d(sinDeg(pol)*sinDeg(azimut), sinDeg(pol)*cosDeg(azimut), 0);
	_deltaEye.normalize();
	_newPos = _eye + _deltaEye;

	if (_clip->posValid(_newPos)) {
		_eye = _newPos;	
		_deltaCenter = Vec3d(sinDeg(pol)*sinDeg(azimut), sinDeg(pol)*cosDeg(azimut), 0);
		_deltaCenter.normalize();
		_center+= _deltaCenter;
		printCoords('w');
		_cam->setViewMatrixAsLookAt(_eye, _center, _up);
	}
}

void CamHandler::stepBackward() {
	_deltaEye = Vec3d(sinDeg(pol)*sinDeg(azimut), sinDeg(pol)*cosDeg(azimut), 0);
	_deltaEye.normalize();	
	_newPos = _eye - _deltaEye;

	if (_clip->posValid(_newPos)) {
		_eye = _newPos;
		_deltaCenter = Vec3d(sinDeg(pol)*sinDeg(azimut), sinDeg(pol)*cosDeg(azimut), 0);
		_deltaCenter.normalize();
		_center-= _deltaCenter;
		printCoords('s');
		_cam->setViewMatrixAsLookAt(_eye, _center, _up);
	}		
}

void CamHandler::stepLeft() {
	_deltaEye = Vec3d(-sinDeg(pol)*cosDeg(azimut), sinDeg(pol)*sinDeg(azimut), 0);
	_deltaEye.normalize();
	_newPos = _eye + _deltaEye;	

	if (_clip->posValid(_newPos)) {
		_eye = _newPos;	
		_deltaCenter = Vec3d(-sinDeg(pol)*cosDeg(azimut), sinDeg(pol)*sinDeg(azimut), 0);
		_deltaCenter.normalize();	
		_center+= _deltaCenter;
		printCoords('a');
		_cam->setViewMatrixAsLookAt(_eye, _center, _up);
	}	
}

void CamHandler::stepRight() {
	_deltaEye = Vec3d(sinDeg(pol)*cosDeg(azimut), -sinDeg(pol)*sinDeg(azimut), 0);
	_deltaEye.normalize();
	_newPos = _eye + _deltaEye;

	if (_clip->posValid(_newPos)) {
		_eye = _newPos;	
		_deltaCenter = Vec3d(sinDeg(pol)*cosDeg(azimut), -sinDeg(pol)*sinDeg(azimut), 0);
		_deltaCenter.normalize();
		_center+= _deltaCenter;
		printCoords('d');
		_cam->setViewMatrixAsLookAt(_eye, _center, _up);
	}	
}

void CamHandler::stepUp() {
	if (_clip->posValid(_eye+Vec3d(0,0, step))) {
		_eye+=Vec3d(0,0, step);
		_center+=Vec3d(0,0, step);
		printCoords('f');
		_cam->setViewMatrixAsLookAt(_eye, _center, _up);	
	}
}

void CamHandler::stepDown() {
	if (_clip->posValid(_eye-Vec3d(0,0, step))) {
		_eye-=Vec3d(0,0, step);
		_center-=Vec3d(0,0, step);
		printCoords('v');	
		_cam->setViewMatrixAsLookAt(_eye, _center, _up);		
	}
}

void CamHandler::lookUp() {	
	decPo();
	printCoords('u');
	updateCam();
}

void CamHandler::lookDown() {
	incPo();
	printCoords('j');
	updateCam();
}


void CamHandler::lookLeft() {	
	decAz();
	printCoords('h');
	updateCam();	
}

void CamHandler::lookRight() {	
	incAz();
	printCoords('k');
	updateCam();
}

void CamHandler::resetCenter() {	
	_center = Vec3d();
	printCoords('r');
	_cam->setViewMatrixAsLookAt(_eye, _center, _up);
}
